/*
 * File:    AbstractHandler.java
 * Created: 04-Jul-2007
 * Version: $Id$
 *
 * COPYRIGHT (C) 2007, Bitgate Software, LLC.  All Rights Reserved.
 *
 * software@bitgatesoftware.com
 */

package com.bitgate.util.service.protocol.http.handler;

import static com.bitgate.util.debug.Debug.debug;
import static com.bitgate.util.debug.Debug.isDebugEnabled;
import static com.bitgate.util.debug.Debug.warning;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import com.bitgate.util.bytes.ByteString;
import com.bitgate.util.file.FileCache;
import com.bitgate.util.mime.MimeTypes;
import com.bitgate.util.service.ServiceVirtualContext;
import com.bitgate.util.service.protocol.http.Http;
import com.bitgate.util.service.protocol.http.HttpClientContext;
import com.bitgate.util.service.protocol.http.HttpDirectory;
import com.bitgate.util.service.protocol.http.HttpVariableStore;
import com.bitgate.util.service.protocol.http.db.Authentication;
import com.bitgate.util.service.protocol.http.render.RenderHookException;
import com.bitgate.util.service.protocol.http.render.RenderHookFactory;
import com.bitgate.util.service.protocol.http.render.RenderHookInterface;
import com.bitgate.util.service.protocol.http.render.RenderHookResult;
import com.bitgate.util.service.protocol.http.store.CookieStore;
import com.bitgate.util.soap.SoapException;
import com.bitgate.util.soap.SoapInvoker;
import com.bitgate.util.soap.SoapRegistry;
import com.bitgate.util.soap.SoapResponse;

/**
 * This is the default handler for HTTP requests, abstracted so that the POST handler can override the POST handling functionality.
 * 
 * @author kenji
 */
public abstract class AbstractHandler implements HandlerInterface
{
	/**
	 * Constructor.
	 */
	public AbstractHandler()
	{
		if (isDebugEnabled()) {
			debug("HttpGetHandler instantiated globally.");
		}
	}
	
	public boolean handleRead(HttpClientContext hcContext) throws HandlerException
	{
		if (hcContext.getParseMode() == HttpClientContext.ParseMode.HEADER) {
			ByteString headerString = null;
			ByteString previousHeader = null;
			byte[] requestData = hcContext.getData();
			int startPosition = 0;
			int endPosition = requestData.length;
			int foundPosition = startPosition;
			boolean needsBodySave = false;
			
			for(int i = startPosition; i < endPosition; i++) {
				if (requestData[i] == '\n') {
					foundPosition = i;
					headerString = handleCutPosition(requestData, startPosition, foundPosition);
					
					startPosition = foundPosition;
					
					if (headerString.length() == 1) {
						hcContext.setParseMode(HttpClientContext.ParseMode.BODY);
						needsBodySave = true;
						break;
					}
					
					if (headerString.indexOf(ByteString.COLON) != -1) {
						int colonIndex = headerString.indexOf(ByteString.COLON);
						ByteString header = headerString.substring(0, colonIndex).toLowerCase();
						
						hcContext.setHeader(header, headerString.substring(colonIndex + 2));
						previousHeader = header;
					} else {
						hcContext.setHeader(previousHeader, headerString);
					}
					
					continue;
				}
			}
			
			if (needsBodySave) {
				while(requestData[foundPosition] == '\r' || requestData[foundPosition] == '\n') {
					foundPosition++;
				}
				
				int remainingDataLength = endPosition - foundPosition;
				byte[] remainingData = new byte[remainingDataLength];
				
				System.arraycopy(requestData, foundPosition, remainingData, 0, remainingDataLength);
				
				hcContext.clearData();
				hcContext.addBufferChunk(remainingData);
			}
		} else if (hcContext.getParseMode() == HttpClientContext.ParseMode.BODY) {
			hcContext.addBufferChunk(hcContext.getData());
		}
		
		if (hcContext.getHeader(new ByteString("content-length")) == null) {
			if (isDebugEnabled()) {
				debug("End of request, no content length posted: '" + hcContext.getOriginalRequest() + "'");
			}
			
			parseCookies(hcContext);
			handleEndOfRequest(hcContext);
			
			return true;
		}
		
		if (isDebugEnabled()) {
			debug("End of request, POST: '" + hcContext.getOriginalRequest() + "'");
			debug("Content-Length for POST = '" + hcContext.getHeader(new ByteString("content-length")).toString() + "'");
		}
		
		boolean endPost = processPostData(hcContext);
		
		if (endPost) {
			parseCookies(hcContext);
			handleEndOfRequest(hcContext);
		}
		
		return endPost;
	}
	
	public boolean handleWrite(final HttpClientContext hcContext) throws HandlerException
	{
		try {
			return hcContext.getClientContext().getClientChannel().signalWrite();
		} catch(IOException e) {
			throw new HandlerException("IOException occurred during write.", e);
		}
	}
	
	private void handleEndOfRequest(HttpClientContext hcContext) throws HandlerException
	{
		String initialRequest = hcContext.getOriginalRequest();
		int initialSpace = initialRequest.indexOf(' ');
		int lastSpace = initialRequest.lastIndexOf(' ');
		
		if (initialSpace == lastSpace) {
			throw new HandlerException("Invalid request '" + initialRequest + "'");
		}
		
		initialRequest = initialRequest.substring(initialSpace + 1, lastSpace);
		
		int getVariableStartPosition = initialRequest.indexOf('?');
		
		if (getVariableStartPosition != -1) {
			String getVariables = initialRequest.substring(getVariableStartPosition + 1);
			
			initialRequest = initialRequest.substring(0, getVariableStartPosition);
			
			if (isDebugEnabled()) {
				debug("Get 'GET' data = '" + getVariables + "'");
			}
			
			HttpVariableStore hvStore = new HttpVariableStore();
			
			hvStore.parse(getVariables, HttpVariableStore.HttpStringType.GET);
			
			hcContext.setVariableStore(hvStore);
		}
		
		// Strip off previous directory attempts.
		initialRequest = initialRequest.replaceAll("\\.\\.\\/", "").replaceAll("\\.\\/", "");
		
		// Strip off repeating slashes to a single one.
		initialRequest = initialRequest.replaceAll("\\/\\/", "\\/");
		
		try {
			initialRequest = URLDecoder.decode(initialRequest, "UTF-8");
		} catch(Exception e) {
			if (isDebugEnabled()) {
				debug("Unable to decode initial request '" + initialRequest + "', possibly malformed or hack attempt?");
			}
		}
		
		if (isDebugEnabled()) {
			debug("Requested file = '" + initialRequest + "'");
		}
		
		ByteString host = hcContext.getHeader(new ByteString("host"));
		String rootDirectory = "/";
		String defaultPage = "";
		String pageTimeout = null;
		String soapRegistry = null;
		String htaccessDatabaseName = null;
		
		if (host == null) {
			throw new HandlerException("No host was specified in the request; failing request.");
		}
		
		ServiceVirtualContext svContext = (ServiceVirtualContext) 
			hcContext.getClientContext().getClientChannel().getProperty(Http.HTTP_VIRTUAL_CONTEXT);
		String foundHost = "*";
		
		if ((foundHost = svContext.findHost(host.toString())) != null) {
			if (isDebugEnabled()) {
				debug("Found host '" + foundHost + "' from request.");
			}
		}
		
		rootDirectory = svContext.getProperty(foundHost, "service.docroot");
		defaultPage = svContext.getProperty(foundHost, "service.default-page");
		soapRegistry = svContext.getProperty(foundHost, "service.soap-registry");
		pageTimeout = svContext.getProperty(foundHost, "cache.page-timeout");
		htaccessDatabaseName = svContext.getProperty(foundHost, "service.htaccess");
		
		hcContext.setProperty("cache.page-timeout", pageTimeout);
		hcContext.setProperty("logger.method", svContext.getProperty(foundHost, "logger.method"));
		hcContext.setProperty("logger.directory", svContext.getProperty(foundHost, "logger.directory"));
		hcContext.setProperty("service.soap-registry", svContext.getProperty(foundHost, "service.soap-registry"));
		hcContext.setProperty("service.htaccess", svContext.getProperty(foundHost, "service.htaccess"));
		
		File initialRequestFile = new File(rootDirectory + initialRequest);
		
		if (isDebugEnabled()) {
			debug("Host='" + host.toString() + "' docRoot='" + rootDirectory + "' DocRoot+Request='" +
				rootDirectory + initialRequest + "' defaultPage='" + defaultPage + "' soapRegistry='" + soapRegistry + "'" +
				" HTAccess='" + htaccessDatabaseName + "'");
		}
		
		// This block of code handles authentication.  As you can see, no page requests will be processed unless the
		// user authenticates appropriately - if applicable.  This code takes the "authorization" header passed in from
		// the request, and applies the logic necessary to check login.  This code always runs when the HTTP(S) stack is
		// used, as it is a top-level protocol for any secondary extensions a server may add.
		
		if (htaccessDatabaseName != null) {
			if (!Authentication.getDefault().authenticate(htaccessDatabaseName, hcContext, rootDirectory, initialRequest)) {
				return;
			}
		} else if (isDebugEnabled()) {
			debug("Bypassing authentication, no htaccess database was defined for this server instance.");
		}
		
		// This block of code will check for a SOAP POST request.  Currently, our SOAP 1.1 functionality only supports
		// a single SOAP POST action, and does not support the use of GET or standard POST.
		
		if (soapRegistry == null || soapRegistry.equals("")) {
			
			// This portion of code handles a standard HTTP request.  The URL given will deem that this request is
			// standard (as the soapRegistry variable is blank.)
			//
			// If the soap registry URL that was given was an actual SOAP URL (determined by the host specified), it
			// will bypass this section of code and handle the output of the SOAP request in the section of code
			// below this block.
			
			File defaultPageFile = new File(rootDirectory + initialRequest + defaultPage);
			int totalBytesRead = 0;
			
			if (defaultPageFile.exists()) {
				if (RenderHookFactory.getDefault().isRenderable(defaultPage)) {
					if (isDebugEnabled()) {
						debug("Request is renderable.");
					}
				}
				
				if (isDebugEnabled()) {
					debug("Request found via default page.");
				}
				
				int periodPosition = initialRequest.lastIndexOf('.');
				String fileExtension = null;
				
				if (periodPosition != -1) {
					fileExtension = initialRequest.substring(periodPosition + 1);
				}
				
				hcContext.setMimeType(MimeTypes.getDefault().get(fileExtension));
				
				try {
					hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
					hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							(int) defaultPageFile.length(), null, defaultPageFile, null).toString().getBytes());
					
					// Pull from cache.
					
					hcContext.write(FileCache.getInstance().getFile(rootDirectory + initialRequest + defaultPage));
				} catch(IOException e) {
					throw new HandlerException("Unable to write data", e);
				}
			} else {
				if (!initialRequestFile.exists() || (!initialRequestFile.isFile() && !initialRequestFile.isDirectory())) {
					StringBuffer pageOut = new StringBuffer();
					
					pageOut.append("Requested page '");
					pageOut.append(initialRequest);
					pageOut.append("' not found.");
					
					hcContext.setMimeType("text/html");
					
					try {
						hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_NOT_FOUND);
						hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_NOT_FOUND, hcContext,
							pageOut.length(), null, null, null).toString().getBytes());
						hcContext.write(pageOut.toString().getBytes());
					} catch(IOException e) {
						throw new HandlerException("Unable to write data", e);
					}
					
					return;
				} else if (initialRequestFile.isDirectory()) {
					if (!initialRequest.endsWith("/")) {
						StringBuffer pageOut = new StringBuffer();
						
						pageOut.append("Your content has moved here: ");
						pageOut.append(initialRequest);
						pageOut.append("<p/>\r\n");
						
						hcContext.setMimeType("text/html");
						
						try {
							hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_MOVED);
							hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_MOVED, hcContext,
								pageOut.length(), initialRequest + "/", initialRequestFile, null).toString().getBytes());
							hcContext.write(pageOut.toString().getBytes());
						} catch(IOException e) {
							throw new HandlerException("Unable to write data", e);
						}
						
						if (isDebugEnabled()) {
							debug("Request '" + initialRequest + "' rewritten to '" + initialRequest + "/', redirected.");
						}
						
						return;
					}
					
					StringBuffer directoryListing = HttpDirectory.listDirectory(initialRequest, rootDirectory);
					
					hcContext.setMimeType("text/html");
					
					try {
						hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
						hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							directoryListing.length(), null, initialRequestFile, null).toString().getBytes());
						hcContext.write(directoryListing.toString().getBytes());
					} catch(IOException e) {
						throw new HandlerException("Unable to write data", e);
					}
					
					return;
				}
				
				File outFile = new File(rootDirectory + initialRequest);
				String extension = null;
				int lastPeriod = -1;
				byte[] renderedData = null;
				
				if ((lastPeriod = initialRequest.lastIndexOf(".")) != -1) {
					extension = initialRequest.substring(lastPeriod + 1);
				} else {
					extension = initialRequest;
				}
				
				// Determine if the request that was made is a request that can be handled by a render hook
				// object.  If so, we render the object with the data given (and the file extension specified)
				// and pass on the request.
				
				RenderHookResult rhResult = null;
				
				if (RenderHookFactory.getDefault().isRenderable(initialRequest)) {
					RenderHookInterface rhInterface = RenderHookFactory.getDefault().getInterface(extension);
					
					if (isDebugEnabled()) {
						debug("Request is renderable via class '" + rhInterface.getClass().getName() + "'");
					}
					
					// Pass current ClientContext
					// Pass current ServerContext
					
					try {
						rhResult = rhInterface.renderPage(rootDirectory + initialRequest, hcContext);
					} catch(RenderHookException ex) {
						throw new HandlerException("Unable to render page: " + ex.getMessage());
					}
				}
				
				if (isDebugEnabled()) {
					debug("Outfile='" + rootDirectory + initialRequest + "' length=" + outFile.length());
				}
				
				int periodPosition = initialRequest.lastIndexOf('.');
				String fileExtension = null;
				
				if (periodPosition != -1) {
					fileExtension = initialRequest.substring(periodPosition + 1);
				}
				
				if (rhResult != null) {
					renderedData = rhResult.getRenderedData();
				}
				
				if (renderedData != null) {
					if (rhResult != null) {
						hcContext.setMimeType(rhResult.getContentType());
					} else {
						hcContext.setMimeType("text/html");
					}
					
					try {
						hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
						hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							renderedData.length, null, null, null).toString().getBytes());
						hcContext.write(renderedData);
					} catch(IOException e) {
						throw new HandlerException("Unable to write data", e);
					}
				} else {
					hcContext.setMimeType(MimeTypes.getDefault().get(fileExtension));
					
					try {
						hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
						hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							(int) outFile.length(), null, outFile, null).toString().getBytes());
						
						hcContext.write(FileCache.getInstance().getFile(rootDirectory + initialRequest));
					} catch(IOException e) {
						throw new HandlerException("Unable to write data", e);
					}
				}
			}
			
			if (isDebugEnabled()) {
				debug("End of read: bytes=" + totalBytesRead);
			}
		} else {
			String soapAction = null;
			
			if (hcContext.getHeader(new ByteString("soapaction")) != null) {
				soapAction = hcContext.getHeader(new ByteString("soapaction")).toString();
				
				if (soapAction.startsWith("\"")) {
					soapAction = soapAction.substring(1);
				}
				
				if (soapAction.endsWith("\"")) {
					soapAction = soapAction.substring(0, soapAction.length() - 1);
				}
				
				if (soapAction.indexOf("://") != -1) {
					soapAction = soapAction.substring(soapAction.indexOf("://") + 3);
					soapAction = soapAction.substring(soapAction.indexOf("/"));
				}
			}
			
			hcContext.setMimeType("text/xml");
			
			// This portion of the code checks to see if the request made is a SOAP registry object.  If the request
			// doesn't match a SOAP URL, the response we send back will be a soap failure object.
			
			if (isDebugEnabled()) {
				debug("SoapRegistry=" + soapRegistry + " initialRequest=" + initialRequest + " soapAction=" + soapAction);
			}
			
			if (SoapRegistry.getDefault().isSoapUrl(soapRegistry, initialRequest)) {
				
				// Handles the posted URL data (whether POST or GET data.)
				
			} else if (soapAction != null && SoapRegistry.getDefault().isSoapUrl(soapRegistry, soapAction)) {
				
				// Handles the posted URL data, which was a POST request, but SOAP-specific.
				
				if (hcContext.getVariableStore() != null) {
					String postedSoapData = hcContext.getVariableStore().getSoapBody();
					String soapMethodName = soapAction.substring(soapAction.lastIndexOf("/") + 1);
				
					// Make sure that the data posted was sent completely.
					
					if (isDebugEnabled()) {
						debug("SoapData '" + postedSoapData + "' methodName='" + soapMethodName + "'");
					}
					
					// Retrieve the class object for the SOAP service.
					
					Class<?> soapClass = SoapRegistry.getDefault().getTopLevelUrlClass(soapRegistry, initialRequest);
					Method soapMethods[] = soapClass.getDeclaredMethods();
					Method soapMethod = null;
					String outputMessage = null;
					
					// Since "getMethod()" doesn't always work here, we'll walk through the list of method objects.
					
					for(Method sMethod : soapMethods) {
						if (sMethod.getName().equals(soapMethodName)) {
							soapMethod = sMethod;
							break;
						}
					}
					
					// If the object was found through a lookup in the class, we invoke it here.
					
					if (soapMethod != null) {
						DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
						XPath xPath = XPathFactory.newInstance().newXPath();
						DocumentBuilder dBuilder = null;
						Document doc = null;
						int numInputs = 0;
						
						try {
							factory.setNamespaceAware(false);
							factory.setValidating(false);
							
							dBuilder = factory.newDocumentBuilder();
						} catch(Exception e) {
							warning("Cannot create a new XML Document Parser.");
							outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Soap Method not found", null);
						}
						
						if (dBuilder != null) {
							try {
								doc = dBuilder.parse(new InputSource(new StringReader(postedSoapData.trim())));
							} catch(Exception e) {
								warning("Cannot parse SOAP request: " + e.getClass().getName());
								outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Posted XML Request invalid", null);
							}
						}
						
						if (doc != null) {
							String inputCount = null;
							
							try {
								inputCount = (String) xPath.evaluate("count(//*[local-name()='input0'])", doc, XPathConstants.STRING);
							} catch(Exception e) {
								warning("Cannot retrieve input variables from posted request: " + e.getClass().getName());
								outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Posted XML Request missing" +
										" input variables.", null);
							}
							
							if (inputCount != null) {
								numInputs = Integer.parseInt(inputCount);
							}
						}
						
						if (doc != null && numInputs == 0) {
							outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Posted XML Request missing" +
									" input variables, or the request is malformed.", null);
						} else {
							Object returnInvocation = null;
							ArrayList<String> inputVariables = new ArrayList<String>();
							
							for(int i = 1; i < (numInputs + 1); i++) {
								try {
									inputVariables.add((String) xPath.evaluate("//*[local-name()='input0'][" + i +
											"]", doc, XPathConstants.STRING));
								} catch(Exception e) {
									if (isDebugEnabled()) {
										debug("Unable to retrive input element " + i + ": " + e.getClass().getName());
									}
								}
							}
							
							try {
								returnInvocation = SoapInvoker.invoke(soapClass, soapMethod, inputVariables);
							} catch(SoapException e) {
								if (isDebugEnabled()) {
									debug("Soap Exception caught: " + e.getMessage());
								}
								
								outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "SOAP method failed to invoke.", null);
							}
							
							if (returnInvocation != null) {
								if (isDebugEnabled()) {
									debug("Soap Vars=" + inputVariables.toString() + "'");
								}
								
								outputMessage = SoapResponse.generateResponse(soapClass, soapMethod, returnInvocation,
										host.toString());
							}
						}
					} else {
						outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Soap Method not found", null);
					}
					
					try {
						if (isDebugEnabled()) {
							debug("Writing response'" + outputMessage + "'");
						}
						
						if (outputMessage != null) {
							hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
							hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
								outputMessage.length(), null, null, null).toString().getBytes());
							hcContext.write(outputMessage.toString().getBytes());
						} else {
							// Should never happen, but you never know!
							throw new HandlerException("Internal error: output message never set.");
						}
					} catch(IOException e) {
						throw new HandlerException("Unable to write data", e);
					}
				}
			} else if (SoapRegistry.getDefault().isTopLevelUrl(soapRegistry, initialRequest) &&
					hcContext.getVariableStore().getGetVariable("wsdl") != null) {
				
				// Returns WSDL output.
				
				if (isDebugEnabled()) {
					debug("Request is a top-level SOAP URL, WSDL is requested.");
				}
				
				String outputMessage = SoapResponse.generateWsdl(soapRegistry, initialRequest, host.toString());
				
				try {
					hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
					hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							outputMessage.length(), null, null, null).toString().getBytes());
					hcContext.write(outputMessage.toString().getBytes());
				} catch(IOException e) {
					throw new HandlerException("Unable to write data", e);
				}
			} else if (SoapRegistry.getDefault().isTopLevelUrl(soapRegistry, initialRequest) &&
					hcContext.getVariableStore().getGetVariable("xsd") != null) {
				
				// Returns XSD output.
				
				if (isDebugEnabled()) {
					debug("Request is a top-level SOAP URL, XSD is requested.");
				}
				
				String outputMessage = SoapResponse.generateXsd(soapRegistry, initialRequest, host.toString());
				
				try {
					hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_OK);
					hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_OK, hcContext,
							outputMessage.length(), null, null, null).toString().getBytes());
					hcContext.write(outputMessage.toString().getBytes());
				} catch(IOException e) {
					throw new HandlerException("Unable to write data", e);
				}
			} else {
				if (isDebugEnabled()) {
					debug("Request is NOT a valid SOAP URL.");
				}
				
				String outputMessage = SoapResponse.generateFailure("SOAP-ENV:Server", "Service Not Found", null);
				
				try {
					hcContext.setRequestResult(HttpHeaders.HeaderResult.HTTP_NOT_FOUND);
					hcContext.write(HttpHeaders.createHeader(HttpHeaders.HeaderResult.HTTP_NOT_FOUND, hcContext,
						outputMessage.length(), null, null, null).toString().getBytes());
					hcContext.write(outputMessage.toString().getBytes());
				} catch(IOException e) {
					throw new HandlerException("Unable to write data", e);
				}
			}
		}
	}

	private ByteString handleCutPosition(byte[] data, int start, int end)
	{
		return new ByteString(data, start, end).trim();
	}
	
	protected void parseCookies(HttpClientContext hcContext) {
		ByteString cookieData = hcContext.getHeader(new ByteString("cookie"));
		
		if (cookieData != null) {
			String[] headerEntries = cookieData.toString().split("; ");
			CookieStore cStore = new CookieStore();
			
			for(String headerEntry : headerEntries) {
				String[] headersSplit = headerEntry.split("=");
				
				if (headersSplit[0].equalsIgnoreCase("expires")) {
					// Handle expiration setting here.
				} else if (headersSplit[0].equalsIgnoreCase("path")) {
					cStore.setPath(headersSplit[1]);
				} else if (headersSplit[0].equalsIgnoreCase("domain")) {
					cStore.setDomain(headersSplit[1]);
				} else {
					if (cStore.getName() != null) {
						hcContext.setIncomingCookie(cStore.getName(), cStore);
						cStore = new CookieStore();
					}
					
					cStore.setName(headersSplit[0]);
					cStore.setValue(headersSplit[1]);
				}
			}
			
			if (cStore.getName() != null) {
				hcContext.setIncomingCookie(cStore.getName(), cStore);
			}
		}
	}
	
	public abstract boolean processPostData(HttpClientContext hcContext);
}